package com.lzm.spring;

import com.lzm.spring.An.Component;
import com.lzm.spring.An.ComponentScan;
import com.lzm.spring.An.Scope;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

public class LzmApplicationContext {
    private Class configClass;
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public LzmApplicationContext(Class configClass) {
        this.configClass = configClass;
//        检查配置类是否有ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String scanPath = componentScanAnnotation.value().replace(".", "/");
//            TODO 检测注解值合法性

//            转目录形式
//            获取该路径在在编译后所在路径
            ClassLoader classLoader = LzmApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(scanPath);
//            获取该路径文件/文件夹
            File fileOrPath = new File(resource.getFile());
//            加载Bean到容器
            loadBean(fileOrPath, scanPath, classLoader);
        } else {
            System.out.println("配置文件无注解");
        }
    }

    public void loadBean(File file, String scanPath, ClassLoader classLoader) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                loadBean(f, scanPath, classLoader);
            }
        } else {
            String filePath = file.getAbsolutePath();
//            获取所有java字节码文件(以包名开头)
            if (filePath.endsWith(".class")) {
                String classPath = filePath.substring(filePath.indexOf(scanPath.replace("/", "\\")), filePath.lastIndexOf(".class")).replace("\\", ".");
                System.out.println(classPath);

                try {
                    Class<?> clazz = classLoader.loadClass(classPath);
                    if (clazz.isAnnotationPresent(Component.class)) {
                        ComponentScan componentAnnotation = clazz.getAnnotation(ComponentScan.class);
                        String beanName = componentAnnotation.value();
                        if (beanName.isEmpty()) {
//                            大驼峰改小驼峰
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }
                        BeanDefinition componentBeanDefinition = new BeanDefinition();
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                            String scope = scopeAnnotation.value();
                            if ("prototype".equals(scope)) {
                                componentBeanDefinition.setScope(scope);
                            } else if ("singleton".equals(scope)) {
                                componentBeanDefinition.setScope(scope);
                            }
                            beanDefinitionMap.put(beanName, componentBeanDefinition);

                        } else {
                            componentBeanDefinition.setScope("singleton");
                        }
                        componentBeanDefinition.setType(clazz);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

//        实例化单例Bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class BeanType = beanDefinition.getType();
        try {
            Object instance = BeanType.getConstructor().newInstance();
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException();
        }
        String scope = beanDefinition.getScope();
        if ("prototype".equals(scope)) {
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        } else if (scope.equals("singleton")) {
            Object bean = singletonObjects.get(beanName);
            if (bean != null) {
                return bean;
            }
            Object singletonBean = createBean(beanName, beanDefinition);
            singletonObjects.put(beanName, singletonBean);
            return singletonBean;
        }
        return null;
    }

}
